/*
 * Copyright 2009 Gordon Turner
 * 
 * This file is part of openwiimote.
 *
 * openwiimote is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * openwiimote is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with Wiimote Simple.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.openwiimote.managers;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.bluetooth.LocalDevice;

import org.apache.log4j.Logger;
import org.openwiimote.Wiimote;
import org.openwiimote.WiimoteDiscoverer;
import org.openwiimote.WiimoteDiscoveryListener;
import org.openwiimote.listeners.WiimoteListenerImpl;

import processing.core.PApplet;

/**
 * @author Gordon Turner
 *
 */
public class WiimoteManager implements WiimoteDiscoveryListener
{
  private PApplet applet;

  public List wiimotes;

  public List wiimoteListeners;

  static Logger logger = Logger.getLogger( WiimoteManager.class );

  static WiimoteDiscoverer wiimoteDiscoverer;

  static Thread wiimoteDiscovererThread;

  private int maxWiimotes;

  public static Boolean stopRequest = new Boolean( false );

  /**
   * The Constructor, usually called in the setup() method in your sketch to
   * initialize and start the library.
   * 
   * @param parentApplet
   */
  public WiimoteManager( PApplet parentApplet )
  {
    logger.debug( "BEGIN" );

    this.applet = parentApplet;

    wiimotes = new ArrayList();

    wiimoteListeners = new ArrayList();

    /*
     * Set "bluecove.jsr82.psm_minimum_off" to "true" to prevent:
     * 
     *  java.lang.IllegalArgumentException: 
     *  PCM values restricted by JAR82 to minimum 4097
     */
    System.setProperty( "bluecove.jsr82.psm_minimum_off", "true" );

    /*
     * Fire up the WiimoteDiscoverer and give the thread a name so we can debug.
     */
    wiimoteDiscoverer = WiimoteDiscoverer.getWiimoteDiscoverer();
    wiimoteDiscoverer.setName( "WiimoteDiscovererThread" );

    /*
     * This class implements the WiimoteDiscoveryListener, so set it in the 
     * WiimoteDiscoverer and start the discovery! :-)
     */
    wiimoteDiscoverer.addWiimoteDiscoveryListener( this );

    applet.registerDispose( this );

    logger.debug( "END" );
  }

  /**
   * 
   */
  public void dispose()
  {
    disconnectAll();
  }

  /**
   * Start off with only one connect method, assume no infrared and no 
   * accelerometer.
   * 
   * This method will block until all wiimotes are found.
   */
  public void connect()
  {
    logger.debug( "BEGIN" );

    // Hardcode max number of wiimotes to search for to 1 for now.
    maxWiimotes = 1;

    System.out.println( "Seaching for " + maxWiimotes + " wiimote(s)." );

    wiimoteDiscoverer.start();

    /*
     * NOTE: This will block until all wiimotes are found.
     */
    logger.debug( "waiting for wiimoteDiscoverer to find all remotes." );
    while( wiimoteDiscoverer.isAlive() )
    {
      if( wiimotes.size() >= maxWiimotes )
      {
        wiimoteDiscoverer.stopWiimoteSearch();
      }
    }

    /*
     * Create a WiimoteListener for each of the wiimotes.
     */
    Wiimote wiimote;
    Iterator iter = wiimotes.iterator();
    while( iter.hasNext() )
    {
      wiimote = (Wiimote) iter.next();
      WiimoteListenerImpl wiimoteListener = new WiimoteListenerImpl();
      wiimote.addListener( wiimoteListener );
      wiimoteListeners.add( wiimoteListener );
    }

    logger.debug( "END" );
  }

  /**
   * This is called by dispose() when the applet is closed.
   * 
   * It is public so that it can be called any time, but be careful, this 
   * will disconnect all the wiimotes.
   */
  public void disconnectAll()
  {
    System.out.println( "Disconnecting all Wiimotes." );
    logger.debug( "Disconnecting all Wiimotes." );

    Wiimote wiimote;

    Iterator iter = wiimotes.iterator();
    while( iter.hasNext() )
    {
      wiimote = (Wiimote) iter.next();
      wiimote.cleanup();
    }
  }

  /**
   * 
   */
  public void hadDisconnects()
  {
    //TODO: Iterate through the wiimoteListeners to see if any have disconnected.
  }

  /**
   * 
   */
  public void isConnecting()
  {
    //TODO: Implement is connecting flag.
  }

  /**
   * 
   */
  public void reconnect()
  {
    //TODO: If hadDisconnects() is true, attempt to connect to wiimotes.
  }

  /**
   * This is the method required for the WiimoteDiscoveryListener interface.
   * 
   * (non-Javadoc)
   * @see org.openwiimote.WiimoteDiscoveryListener#wiimoteDiscovered(org.openwiimote.Wiimote)
   */
  public void wiimoteDiscovered( Wiimote wiimote )
  {
    logger.debug( "BEGIN" );
    logger.debug( Thread.currentThread().getName() );

    if( !wiimotes.contains( wiimote ) )
    {
      logger.debug( "Adding wiimote " + wiimotes.size() + 1 );
      wiimote.setLight( wiimotes.size() );
      wiimotes.add( wiimote );
    }

    logger.debug( "END" );
  }

  /**
   * Diagnostic method.
   */
  public void runDiagnostics()
  {

    System.out.println();
    System.out.println( "OS details" );
    System.out.println( "----------" );
    System.out.println( "os.name: " + System.getProperty( "os.name" ) );
    System.out.println( "os.arch: " + System.getProperty( "os.arch" ) );
    System.out.println( "os.version: " + System.getProperty( "os.version" ) );
    System.out.println();
    System.out.println( "Java details" );
    System.out.println( "------------" );
    System.out
        .println( "java.version: " + System.getProperty( "java.version" ) );
    System.out.println( "java.vendor: " + System.getProperty( "java.vendor" ) );
    System.out.println( "java.class.path: " );
    String[] classPath = System.getProperty( "java.class.path" ).split( ";" );
    for( int i = 0; i < classPath.length; i++ )
    {
      System.out.println( " " + classPath[ i ] );
    }
    System.out.println();
    System.out.println( "Bluetooth details" );
    System.out.println( "-----------------" );
    try
    {
      // See: http://java.sun.com/javame/technology/msa/jsr82.jsp  
      System.out.println( "bluetooth.api.version: "
          + System.getProperty( "bluetooth.api.version" ) );
    }
    catch( Exception e )
    {
      System.out.println( "Error! There was a problem getting some Bluetooth "
          + "details from this machine, are the Bluetooth drivers installed?" );
    }
    try
    {
      // Bluecove provides some diagnostic details, not sure about other stacks. 
      System.out.println( LocalDevice.getProperty( "bluecove.stack" ) );
    }
    catch( Exception e )
    {
      // Do nothing. } 
    }
  }
}
